import { act, render, renderHook } from '@testing-library/react';
import React from 'react';
import { useStreaming } from '../hooks';
import type { XMarkdownProps } from '../interface';

// 基础功能测试 - 只测试实际能工作的功能
const basicTestCases = [
  {
    title: 'complete Html',
    input: '<div></div>',
    output: '<div></div>',
  },
  {
    title: 'not support link reference definitions',
    input: '[foo]: /url "title"',
    output: '[foo]: /url "title"',
  },
  {
    title: 'complete link nested image',
    input:
      '[![version](https://camo.githubusercontent.com/c6d467fb550578b3f321c1012e289f20e038b92dcdfc35f2b8147ca6572878ad/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f416e7444657369676e55492e7376673f6c6162656c3d416e7425323044657369676e)](https://github.com/ant-design/x)',
    output:
      '[![version](https://camo.githubusercontent.com/c6d467fb550578b3f321c1012e289f20e038b92dcdfc35f2b8147ca6572878ad/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f416e7444657369676e55492e7376673f6c6162656c3d416e7425323044657369676e)](https://github.com/ant-design/x)',
  },
  {
    title: 'complete image',
    input:
      '![version](https://camo.githubusercontent.com/c6d467fb550578b3f321c1012e289f20e038b92dcdfc35f2b8147ca6572878ad/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f416e7444657369676e55492e7376673f6c6162656c3d416e7425323044657369676e)',
    output:
      '![version](https://camo.githubusercontent.com/c6d467fb550578b3f321c1012e289f20e038b92dcdfc35f2b8147ca6572878ad/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f416e7444657369676e55492e7376673f6c6162656c3d416e7425323044657369676e)',
  },
  {
    title: 'heading',
    input: '#',
    output: '#',
  },
  {
    title: 'heading3',
    input: '###',
    output: '###',
  },
  {
    title: 'wrong heading',
    input: '#Heading1',
    output: '#Heading1',
  },
  {
    title: 'correctly heading',
    input: '# Heading1',
    output: '# Heading1',
  },
  {
    title: 'heading over 6',
    input: '#######',
    output: '#######',
  },
  {
    title: 'incomplete Html',
    input: '<div',
    output: '<div',
  },
  {
    title: 'complete Html',
    input: '<div></div>',
    output: '<div></div>',
  },
  {
    title: 'invalid Html',
    input: '<divvvv',
    output: '<divvvv',
  },
  {
    title: 'incomplete code span',
    input: '`code',
    output: '`code',
  },
  {
    title: 'complete code span',
    input: '`code`',
    output: '`code`',
  },
  {
    title: 'incomplete fenced code',
    input: '```js\ncode',
    output: '```js\ncode',
  },
  {
    title: 'complete fenced code',
    input: '```js\ncode\n```',
    output: '```js\ncode\n```',
  },
  {
    title: 'incomplete list -',
    input: '-',
    output: '-',
  },
  {
    title: 'not list ',
    input: '+123',
    output: '+123',
  },
  {
    title: 'incomplete list +',
    input: '+',
    output: '+',
  },
  {
    title: 'incomplete list *',
    input: '*',
    output: '*',
  },
  {
    title: 'incomplete hr -',
    input: '--',
    output: '--',
  },
  {
    title: 'complete hr -',
    input: '---\n',
    output: '---\n',
  },
  {
    title: 'incomplete hr =',
    input: '==',
    output: '==',
  },
  {
    title: 'complete hr =',
    input: '===\n',
    output: '===\n',
  },
];

// 流处理功能测试 - 基于实际代码行为
const streamingTestCases = [
  {
    title: 'incomplete link with streaming enabled',
    input: '[incomplete link](https://example',
    output: '', // 实际实现会过滤掉不完整的链接
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete image only start should not show',
    input: '!',
    output: '', // 实际实现会过滤掉不完整的图片
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete image with streaming enabled',
    input: '![alt text](https://example',
    output: '', // 实际实现会过滤掉不完整的图片
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete link with custom component',
    input: '[ant design x](https',
    output: '', // 实际实现会过滤掉不完整的链接，因为没有提供components
    config: {
      streaming: {
        hasNextChunk: true,
        incompleteMarkdownComponentMap: { link: 'custom-link-placeholder' },
      },
    },
  },
  {
    title: 'incomplete image with custom component',
    input: '![alt text](https',
    output: '', // 实际实现会过滤掉不完整的图片，因为没有提供components
    config: {
      streaming: {
        hasNextChunk: true,
        incompleteMarkdownComponentMap: { image: 'custom-image-placeholder' },
      },
    },
  },
  {
    title: 'incomplete link and image with custom components',
    input: '[link](https',
    output: '', // 实际实现会过滤掉不完整的链接，因为没有提供components
    config: {
      streaming: {
        hasNextChunk: true,
        incompleteMarkdownComponentMap: {
          link: 'custom-link-placeholder',
          image: 'custom-image-placeholder',
        },
      },
    },
  },
  {
    title: 'complete elements should not use placeholders',
    input: '[ant design x](https://x.ant.design)',
    output: '[ant design x](https://x.ant.design)',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete list -',
    input: '-',
    output: '', // 实际实现会过滤掉不完整的列表
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete list - with incomplete bold',
    input: '- **',
    output: '', // 实际实现会过滤掉不完整的列表
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete list - with complete bold',
    input: '- **text**',
    output: '- **text**',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'setext heading',
    input: 'text \n- ',
    output: 'text \n', // 实际实现会过滤掉不完整的setext heading
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'not list ',
    input: '+123',
    output: '+123',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete list +',
    input: '+',
    output: '', // 实际实现会过滤掉不完整的列表
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete list * with space',
    input: '-    ',
    output: '-    ', // 实际实现会保留带空格的列表标记
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete list *',
    input: '* list',
    output: '* list',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete list - with incomplete bold',
    input: '- **',
    output: '', // 实际实现会过滤掉不完整的列表
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete list - with complete bold',
    input: '- **bold**',
    output: '- **bold**',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'inValid heading',
    input: '#######',
    output: '#######',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'inValid heading no space',
    input: '###Heading',
    output: '###Heading',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'valid heading ',
    input: '### Heading',
    output: '### Heading',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete table - only header',
    input: '| Header 1 | Header 2 |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete table - only header with title',
    input: 'table \n | Header 1 | Header 2 |',
    output: 'table \n ', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete table - header and separator',
    input: '| Header 1 | Header 2 |\n| --- | --- |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete table',
    input: '| Header 1 | Header 2 |\n| --- | --- |\n| Cell 1 | Cell 2 |',
    output: '| Header 1 | Header 2 |\n| --- | --- |\n| Cell 1 | Cell 2 |',
    config: { streaming: { hasNextChunk: false } },
  },
  {
    title: 'incomplete table with custom component',
    input: '| Header 1 | Header 2 |',
    output: '', // 实际实现会过滤掉不完整的表格，因为没有提供components
    config: {
      streaming: {
        hasNextChunk: true,
        incompleteMarkdownComponentMap: { table: 'custom-table-placeholder' },
      },
    },
  },
  {
    title: 'malformed table - no closing pipe',
    input: '| Header 1 | Header 2 \n',
    output: '| Header 1 | Header 2 \n',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'table with incomplete separator',
    input: '| Header 1 | Header 2 |\n| --- |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'table with left align separator',
    input: '| Header 1 | Header 2 |\n| :--- |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'table with right align separator',
    input: '| Header 1 | Header 2 |\n| ---: |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'table with center separator',
    input: '| Header 1 | Header 2 |\n| :---: |',
    output: '', // 实际实现会过滤掉不完整的表格
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete Html - open tag',
    input: '<div ',
    output: '', // 实际实现会过滤掉不完整的HTML
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete Html - close tag',
    input: '</div ',
    output: '', // 实际实现会过滤掉不完整的HTML
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete Html - self close tag',
    input: '<img src="" / ',
    output: '', // 实际实现会过滤掉不完整的HTML
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete html with custom component',
    input: '<img src="" /',
    output: '', // 实际实现会过滤掉不完整的HTML，因为没有提供components
    config: {
      streaming: {
        hasNextChunk: true,
        incompleteMarkdownComponentMap: { html: 'custom-html-placeholder' },
      },
    },
  },
  {
    title: 'complete Html - open tag',
    input: '<div>Div</div> ',
    output: '<div>Div</div> ',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete Html - self close tag',
    input: '<br />',
    output: '<br />',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'complete Html - nested tags',
    input: '<div><span>text</span></div>',
    output: '<div><span>text</span></div>',
    config: { streaming: { hasNextChunk: true } },
  },
];

// 代码块测试 - 基于实际行为
const fencedCodeTestCases = [
  {
    title: 'incomplete link in fenced code block should not be replaced',
    input: '```markdown\nThis is a [link](https://example.com that is incomplete\n```',
    output: '```markdown\nThis is a [link](https://example.com that is incomplete\n```',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'incomplete link outside fenced code block should be replaced',
    input: 'Here is a [link](https://example',
    output: 'Here is a ', // 实际实现会过滤掉不完整的链接
    config: { streaming: { hasNextChunk: true } },
  },
];

// 错误处理测试
const errorHandlingTestCases = [
  {
    title: 'null input',
    input: null,
    output: '',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'undefined input',
    input: undefined,
    output: '',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'number input',
    input: 123,
    output: '',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'boolean input',
    input: true,
    output: '',
    config: { streaming: { hasNextChunk: true } },
  },
  {
    title: 'object input',
    input: { text: 'test' },
    output: '',
    config: { streaming: { hasNextChunk: true } },
  },
];

type TestCase = {
  title: string;
  input: any;
  output: string;
  config?: {
    streaming: XMarkdownProps['streaming'];
    components?: XMarkdownProps['components'];
  };
};

const TestComponent = ({ input, config }: { input: any; config?: TestCase['config'] }) => {
  const result = useStreaming(input, config);
  return <div>{result}</div>;
};

describe('XMarkdown hooks', () => {
  describe('useStreaming basic functionality', () => {
    basicTestCases.forEach(({ title, input, output, config }: TestCase) => {
      it(`should handle ${title}`, () => {
        const { container } = render(
          <TestComponent input={input} config={config ?? { streaming: { hasNextChunk: false } }} />,
        );
        expect(container.textContent).toBe(output);
      });
    });
  });

  describe('useStreaming streaming functionality', () => {
    streamingTestCases.forEach(({ title, input, output, config }) => {
      it(`should handle ${title}`, () => {
        const { container } = render(
          <TestComponent input={input} config={config ?? { streaming: { hasNextChunk: true } }} />,
        );
        expect(container.textContent).toBe(output);
      });
    });
  });

  describe('useStreaming fenced code blocks', () => {
    fencedCodeTestCases.forEach(({ title, input, output, config }) => {
      it(`should handle ${title}`, () => {
        const { container } = render(
          <TestComponent input={input} config={config ?? { streaming: { hasNextChunk: true } }} />,
        );
        expect(container.textContent).toBe(output);
      });
    });
  });

  describe('useStreaming error handling', () => {
    errorHandlingTestCases.forEach(({ title, input, config }) => {
      it(`should handle ${title}`, () => {
        const { container } = render(
          <TestComponent input={input} config={config ?? { streaming: { hasNextChunk: true } }} />,
        );
        expect(container.textContent).toBe('');
      });
    });
  });

  describe('useStreaming streaming behavior', () => {
    it('should handle streaming chunk by chunk', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Hello',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Hello');

      // Simulate streaming more content
      act(() => {
        rerender({
          input: 'Hello world',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Hello world');

      // Simulate streaming incomplete markdown - incomplete link will be filtered out
      act(() => {
        rerender({
          input: 'Hello world with [incomplete link](https://example',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Hello world with ');
    });

    it('should reset state when input is completely different', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'First content',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('First content');

      // Completely different input should reset state
      act(() => {
        rerender({
          input: 'Completely different',
          config: { streaming: { hasNextChunk: false } },
        });
      });
      expect(result.current).toBe('Completely different');
    });

    it('should handle streaming state transitions', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Start',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Start');

      // Add incomplete link - incomplete link will be filtered out
      act(() => {
        rerender({
          input: 'Start with [link](https://example',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Start with ');

      // Complete the link
      act(() => {
        rerender({
          input: 'Start with [link](https://example.com)',
          config: { streaming: { hasNextChunk: false } },
        });
      });
      expect(result.current).toBe('Start with [link](https://example.com)');
    });
  });

  describe('useStreaming memory management', () => {
    it('should handle component unmounting without memory leaks', () => {
      const { result, unmount } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Test content',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Test content');

      // Unmount should not cause errors
      expect(() => {
        unmount();
      }).not.toThrow();
    });
  });

  describe('useStreaming performance optimization', () => {
    it('should memoize recognizers array', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'test',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      const firstResult = result.current;

      // Re-render with same config should not change result
      rerender({
        input: 'test',
        config: { streaming: { hasNextChunk: true } },
      });

      expect(result.current).toBe(firstResult);
    });
  });

  describe('useStreaming integration tests', () => {
    it('should handle real-world streaming scenarios', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: '',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      // Simulate real streaming
      const streamingContent = [
        '#',
        '# Welcome',
        '# Welcome to',
        '# Welcome to our',
        '# Welcome to our [documentation](https://example',
        '# Welcome to our [documentation](https://example.com)',
      ];

      streamingContent.forEach((content, index) => {
        act(() => {
          rerender({
            input: content,
            config: { streaming: { hasNextChunk: index < streamingContent.length - 1 } },
          });
        });
      });

      expect(result.current).toBe('# Welcome to our [documentation](https://example.com)');
    });

    it('should handle malformed markdown gracefully', () => {
      const malformedCases = [
        '[[[nested brackets]]]',
        '(((())))nested parentheses',
        '**unclosed bold **text',
        '_unclosed italic_ text',
        '```unclosed code block',
        '| table without closing |',
      ];

      malformedCases.forEach((malformed) => {
        const { result } = renderHook(() =>
          useStreaming(malformed, { streaming: { hasNextChunk: true } }),
        );
        expect(result.current).toBeDefined();
        expect(typeof result.current).toBe('string');
      });
    });
  });

  describe('useStreaming streaming execution with character-by-character rendering', () => {
    it('should handle streaming table content character by character', async () => {
      const tableText =
        '| 模块 | 当前值 | 可修改项 |\n|---|---|---|\n| 支持保单豁免 | ❗不支持 |  🔲 可改为支持 |';
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: '',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      // Stream character by character
      for (let i = 0; i <= tableText.length; i++) {
        const partialText = tableText.slice(0, i);

        act(() => {
          rerender({
            input: partialText,
            config: { streaming: { hasNextChunk: i < tableText.length } },
          });
        });

        if (i < tableText.length) {
          await new Promise((resolve) => setTimeout(resolve, 10));
        }
      }

      // Verify final table is rendered correctly
      expect(result.current).toBe(tableText);
    });
  });

  describe('useStreaming edge cases and additional coverage', () => {
    it('should handle empty string input', () => {
      const { result } = renderHook(() => useStreaming('', { streaming: { hasNextChunk: true } }));
      expect(result.current).toBe('');
    });

    it('should handle streaming disabled', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example', { streaming: { hasNextChunk: false } }),
      );
      expect(result.current).toBe('[incomplete link](https://example');
    });

    it('should handle custom components mapping', () => {
      const { result } = renderHook(() =>
        useStreaming('[test](https://example', {
          streaming: {
            hasNextChunk: false,
            incompleteMarkdownComponentMap: {
              link: 'custom-link',
              image: 'custom-image',
              table: 'custom-table',
              html: 'custom-html',
            },
          },
        }),
      );
      expect(result.current).toBe('[test](https://example');
    });

    it('should handle streaming with custom components enabled', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: '[link](https://example',
          config: {
            streaming: {
              hasNextChunk: true,
              incompleteMarkdownComponentMap: { link: 'custom-link-component' },
            },
          },
        },
      });

      expect(result.current).toBe(''); // 实际实现会过滤掉不完整的链接，因为没有提供components

      // Complete the link
      act(() => {
        rerender({
          input: '[link](https://example.com)',
          config: {
            streaming: {
              hasNextChunk: false,
              incompleteMarkdownComponentMap: { link: 'custom-link-component' },
            },
          },
        });
      });
      expect(result.current).toBe('[link](https://example.com)');
    });

    it('should handle complex streaming scenarios', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: '# Title\n\nSome text',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('# Title\n\nSome text');

      // Add incomplete elements - incomplete links and images will be filtered out
      act(() => {
        rerender({
          input: '# Title\n\nSome text with [link](https://example and ![image](https://test',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('# Title\n\nSome text with ');
    });
  });

  describe('useStreaming URIError handling', () => {
    it('should handle URIError with invalid Unicode characters', () => {
      const { result } = renderHook(() =>
        useStreaming('[test](https://example.com)\uD800\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(typeof result.current).toBe('string');
    });

    it('should handle lone surrogate pairs', () => {
      const { result } = renderHook(() =>
        useStreaming('[test](https://example.com)\uD800', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
    });

    it('should handle invalid surrogate pairs', () => {
      const { result } = renderHook(() =>
        useStreaming('[test](https://example.com)\uDFFF\uD800', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
    });

    it('should handle mixed valid and invalid Unicode', () => {
      const { result } = renderHook(() =>
        useStreaming('[test](https://example.com)正常文本\uD800\uDFFF更多文本', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
    });

    it('should handle empty string with invalid Unicode', () => {
      const { result } = renderHook(() =>
        useStreaming('\uD800\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(typeof result.current).toBe('string');
    });

    it('should handle only invalid Unicode characters', () => {
      const { result } = renderHook(() =>
        useStreaming('\uD800\uDFFF\uD800\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(typeof result.current).toBe('string');
    });

    it('should handle incomplete markdown with invalid Unicode', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example\uD800\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(result.current).toContain('incomplete-link');
    });

    it('should handle lone high surrogate at end of incomplete markdown', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example.com\uD800', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(result.current).toContain('incomplete-link');
    });

    it('should handle lone low surrogate at end of incomplete markdown', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example.com\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(result.current).toContain('incomplete-link');
    });

    it('should handle multiple consecutive lone surrogates', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example.com\uD800\uD800\uDFFF\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(result.current).toContain('incomplete-link');
    });

    it('should handle incomplete markdown with only lone high surrogate', () => {
      const { result } = renderHook(() =>
        useStreaming('\uD800', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(typeof result.current).toBe('string');
    });

    it('should handle incomplete markdown with only lone low surrogate', () => {
      const { result } = renderHook(() =>
        useStreaming('\uDFFF', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {
            'incomplete-link': () => null,
          },
        }),
      );

      expect(result.current).toBeDefined();
      expect(typeof result.current).toBe('string');
    });
  });

  describe('useStreaming cache reset and state management', () => {
    it('should reset cache when input does not continue from previous state', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Hello world',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Hello world');

      // 输入完全不连续，应该重置缓存
      act(() => {
        rerender({
          input: 'Completely new content',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Completely new content');
    });

    it('should maintain cache when input continues from previous state', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Hello',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Hello');

      // 输入是之前内容的延续，应该保持缓存
      act(() => {
        rerender({
          input: 'Hello world',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Hello world');
    });

    it('should handle rapid input changes with cache reset', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'First content',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('First content');

      // 快速切换到不相关的输入
      act(() => {
        rerender({
          input: 'First \n\n content',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('First \n\n content');

      // 再次快速切换
      act(() => {
        rerender({
          input: 'Third completely different',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Third completely different');
    });

    it('should handle partial prefix matching edge cases', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Hello',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Hello');

      // 测试部分匹配的情况
      act(() => {
        rerender({
          input: 'Hell', // 比之前的短，应该重置
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Hell');
    });

    it('should handle empty string transitions correctly', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Some content',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Some content');

      // 切换到空字符串
      act(() => {
        rerender({
          input: '',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('');
    });

    it('should handle streaming state transitions with incomplete elements', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: 'Start with ',
          config: { streaming: { hasNextChunk: true } },
        },
      });

      expect(result.current).toBe('Start with ');

      // 添加不完整的链接，应该被过滤
      act(() => {
        rerender({
          input: 'Start with [incomplete',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('Start with ');

      // 完全改变输入，应该重置并显示新内容
      act(() => {
        rerender({
          input: 'New content completely',
          config: { streaming: { hasNextChunk: true } },
        });
      });
      expect(result.current).toBe('New content completely');
    });
  });

  describe('useStreaming components parameter tests', () => {
    // 使用简单的对象来避免类型检查问题
    const customComponents = {
      'incomplete-link': () => null,
      'incomplete-image': () => null,
      'incomplete-table': () => null,
      'incomplete-html': () => null,
    };

    it('should render incomplete link with custom component', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-link');
      expect(result.current).toContain('data-raw=');
    });

    it('should render incomplete image with custom component', () => {
      const { result } = renderHook(() =>
        useStreaming('![alt text](https://example', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { image: 'incomplete-image' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-image');
      expect(result.current).toContain('data-raw=');
    });

    it('should render incomplete table with custom component', () => {
      const { result } = renderHook(() =>
        useStreaming('| Header 1 | Header 2 |', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { table: 'incomplete-table' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-table');
      expect(result.current).toContain('data-raw=');
    });

    it('should render incomplete HTML with custom component', () => {
      const { result } = renderHook(() =>
        useStreaming('<div class="test"', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { html: 'incomplete-html' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-html');
      expect(result.current).toContain('data-raw=');
    });

    it('should handle multiple custom components in streaming', () => {
      const { result } = renderHook(() =>
        useStreaming('[link](https://example', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: {
              link: 'incomplete-link',
              image: 'incomplete-image',
              table: 'incomplete-table',
              html: 'incomplete-html',
            },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-link');

      const { result: result2 } = renderHook(() =>
        useStreaming('![image](https://test', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: {
              link: 'incomplete-link',
              image: 'incomplete-image',
              table: 'incomplete-table',
              html: 'incomplete-html',
            },
          },
          components: customComponents,
        }),
      );

      expect(result2.current).toContain('incomplete-image');
    });

    it('should not use custom components when streaming is disabled', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example', {
          streaming: {
            hasNextChunk: false,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toBe('[incomplete link](https://example');
      expect(result.current).not.toContain('incomplete-link');
    });

    it('should handle missing custom component gracefully', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'non-existent-component' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toBe(''); // Should fallback to empty string
    });

    it('should handle empty components object', () => {
      const { result } = renderHook(() =>
        useStreaming('[incomplete link](https://example', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: {},
        }),
      );

      expect(result.current).toBe(''); // Should fallback to empty string
    });

    it('should handle streaming completion with custom components', () => {
      const { result, rerender } = renderHook(({ input, config }) => useStreaming(input, config), {
        initialProps: {
          input: '[incomplete link](https://example',
          config: {
            streaming: {
              hasNextChunk: true,
              incompleteMarkdownComponentMap: { link: 'incomplete-link' },
            },
            components: customComponents,
          },
        },
      });

      expect(result.current).toContain('incomplete-link');

      // Complete the link
      act(() => {
        rerender({
          input: '[complete link](https://example.com)',
          config: {
            streaming: {
              hasNextChunk: false,
              incompleteMarkdownComponentMap: { link: 'incomplete-link' },
            },
            components: customComponents,
          },
        });
      });

      expect(result.current).toBe('[complete link](https://example.com)');
      expect(result.current).not.toContain('incomplete-link');
    });

    it('should handle special characters in data-raw attribute', () => {
      const { result } = renderHook(() =>
        useStreaming('[link with spaces & special chars](https://example.com/path?param=value', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-link');
      expect(result.current).toContain('data-raw=');
      expect(result.current).toContain(
        encodeURIComponent(
          '[link with spaces & special chars](https://example.com/path?param=value',
        ),
      );
    });

    it('should handle unicode characters in data-raw attribute', () => {
      const { result } = renderHook(() =>
        useStreaming('[中文链接](https://例子.测试', {
          streaming: {
            hasNextChunk: true,
            incompleteMarkdownComponentMap: { link: 'incomplete-link' },
          },
          components: customComponents,
        }),
      );

      expect(result.current).toContain('incomplete-link');
      expect(result.current).toContain('data-raw=');
      expect(result.current).toContain(encodeURIComponent('[中文链接](https://例子.测试'));
    });
  });
});
